#include "temu.h"

/* We use the POSIX regex functions to process regular expressions.
 * Type 'man regex' for more information about POSIX regex functions.
 */
#include <sys/types.h>
#include <regex.h>

enum {
	NOTYPE = 256, EQ, NUM,HEX,OR,AND,NEQ,NEG,REG,POI
	/* TODO: Add more token types */
};

static struct rule {
	char *regex;
	int token_type;
	int priority;
} rules[] = {

	/* TODO: Add more rules.
	 * Pay attention to the precedence level of different rules.
	 */
	{"\\b[0-9]+\\b",NUM,0},	  //DEC number
	{"\\b0[xX][0-9a-fA-F]+\\b",HEX,0} ,//HEX number
	{"\\$[a-z0-9]+",REG,0},                          //register
	{" +",	NOTYPE,0},			     // spaces
	{"\\+", '+',4},				        	// plus, because in regex '\' is needed, and \ in c need \ .
	{"-",'-',4},
	{"\\*",'*',5},
	{"/",'/',5},
	{"&&",AND,2},
	{"\\|\\|",OR,1},
	{"==", EQ,3},						      // equal
	{"!=",NEQ,3},
	{"!",'!',6},
	{"\\(", '(',7},
	{"\\)", ')',7}
};

#define NR_REGEX (sizeof(rules) / sizeof(rules[0]) )

static regex_t re[NR_REGEX];

/* Rules are used for many times.
 * Therefore we compile them only once before any usage.
 */
void init_regex() {
	int i;
	char error_msg[128];
	int ret;

	for(i = 0; i < NR_REGEX; i ++) {
		ret = regcomp(&re[i], rules[i].regex, REG_EXTENDED);
		if(ret != 0) {
			regerror(ret, &re[i], error_msg, 128);
			Assert(ret == 0, "regex compilation failed: %s\n%s", error_msg, rules[i].regex);
		}
	}
}

typedef struct token {
	int type;
	char str[32];
	int priority;
} Token;

Token tokens[32];
int nr_token;

static bool make_token(char *e) {
	int position = 0;
	int i;
	regmatch_t pmatch;
	
	nr_token = 0;

	while(e[position] != '\0') {
		/* Try all rules one by one. */
		for(i = 0; i < NR_REGEX; i ++) {
			if(regexec(&re[i], e + position, 1, &pmatch, 0) == 0 && pmatch.rm_so == 0) {
				char *substr_start = e + position;
				int substr_len = pmatch.rm_eo;
				char* REGS=e+position;
				Log("match rules[%d] = \"%s\" at position %d with len %d: %.*s", i, rules[i].regex, position, substr_len, substr_len, substr_start);
				position += substr_len;

				/* TODO: Now a new token is recognized with rules[i]. Add codes
				 * to record the token in the array `tokens'. For certain types
				 * of tokens, some extra actions should be performed.
				 */

				switch(rules[i].token_type) {
					case NOTYPE : break;
					case REG:
					tokens[nr_token].type=rules[i].token_type;
					tokens[nr_token].priority=rules[i].priority;
					strncpy(tokens[nr_token].str,REGS,substr_len);
					tokens[nr_token].str[substr_len]='\0';
					nr_token++;
					break;
					default: tokens[nr_token].type=rules[i].token_type; 
									 tokens[nr_token].priority=rules[i].priority;
									 strncpy(tokens[nr_token].str,substr_start,substr_len);
									 tokens[nr_token].str[substr_len]='\0';
									//  printf("tokens %s 's type = %d\n",tokens[nr_token].str,tokens[nr_token].type);
									 nr_token++;
				}

				break;
			}
		}

		if(i == NR_REGEX) {
			printf("no match at position %d\n%s\n%*.s^\n", position, e, position, "");
			return false;
		}
	}

	return true; 
}

int dominant_op(int p, int q)
{
	int min=23; int i=0;
	int counter=0; int op=p;
	for(i=p;i<=q;i++){
		if(tokens[i].type==NUM||tokens[i].type==HEX||tokens[i].type==REG) continue;
		if(tokens[i].type=='(') counter++;
		if(tokens[i].type==')') counter--;
		if(counter!=0) continue;
		if(tokens[i].priority<min) {
			min=tokens[i].priority;
			op=i;
		}
 	}
	return op;
}

bool check_parentheses(int p, int q)
{
	int counter=0;
	int i=0;
	if(tokens[p].type!='('||tokens[q].type!=')'){
		// printf("Parentheses not matched,%s, %s\n",tokens[p].str,tokens[q].str);
		return false;
	}
	for(i =p; i<=q; i++){
		if(tokens[i].type=='(') counter++;
		else if(tokens[i].type==')') counter--;

		if(counter==0&&i<q){
			printf("check_parentheses false\n");
			return false;
		}
	}
	if(counter!=0) return false;
	else return true;	
}

uint32_t eval(int p, int q)
{
	if(p>q){
		printf("bad expression\n");
		return -1;
	}
	else if (p==q){
		uint32_t outcome=0;
		int i;
	
		if(tokens[p].type==HEX)	sscanf(tokens[p].str,"%x",&outcome);
		if(tokens[p].type==NUM) sscanf(tokens[p].str,"%d",&outcome);
		if(tokens[p].type==REG){
			for(i=0;i<32;i++){
				if(strcmp(tokens[p].str,regfile[i])==0)		break;
			}
			outcome=cpu.gpr[i]._32;
			// printf("eval %s=%08x\n",tokens[p].str,outcome);
		} 
		// printf("type=%d, outcome=%d\n",tokens[p].type,outcome);
		return outcome;
	}

	else if (check_parentheses(p,q)==true){
		return eval(p+1,q-1);
	}
	else{
		int op=dominant_op(p,q);
		if(op==p||tokens[op].type==NEG||tokens[op].type==POI||tokens[op].type=='!'){
			uint32_t exp=eval(p+1,q);
			switch(tokens[op].type){
			case POI:return mem_read(exp,4);
			case NEG:return -exp;
			case '!':return !exp;
			}
		}
		uint32_t exp1=eval(p,op-1);
		uint32_t exp2=eval(op+1,q);
		switch(tokens[op].type){
			case '+':return exp1+exp2;
			case '-':return exp1-exp2;
			case '*':return exp1*exp2;
			case '/':return exp1/exp2;
			case OR :return exp1||exp2;
			case AND:return exp1&&exp2;
			case EQ :return exp1==exp2;
			case NEQ:return exp1!=exp2;
			default: break;
		}
	}
	assert(1);
	return -1;
}

uint32_t expr(char *e, bool *success) {
	if(!make_token(e)) {
		*success = false;
		return 0;
	}
	int i=0;
	for(i=0;i<nr_token;i++){
		if(tokens[i].type=='-'&&(tokens[i-1].type!=NUM&&tokens[i-1].type!=HEX&&tokens[i-1].type!=')')){
			tokens[i].type=NEG;
			tokens[i].priority=4;	
		}
		if(tokens[i].type=='*'&&(i==0||(tokens[i-1].type!=NUM&&tokens[i-1].type!=HEX&&tokens[i-1].type!=')'))){
			tokens[i].type=POI;
			tokens[i].priority=4;
		}

	}
	/* TODO: Insert codes to evaluate the expression. */
	*success=true;
	return eval(0,nr_token-1);
}

